package ru.niir.goodfil.ui;

import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import ru.niir.goodfil.db.*;
import ru.niir.goodfil.db.handlers.Goodfil;
import ru.niir.goodfil.exceptions.MailerException;
import ru.niir.goodfil.ui.utils.FilePublisher;
import ru.niir.goodfil.ui.utils.RestsExportManager;

import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

public class RestsExportBean extends PageBean {
    private static final Logger logger = Logger.getLogger("RestsExportBean");

    public static final String TEMP_CATALOG = ".temp";
    public static final String DEFAULT_TEMP_CATALOG = "C:\\Temp";
    private static final String TEMPLATE_PATH = ".rests.export.template";
    private static final String TEMPLATE_PATH_DEFAULT = "C:\\template.xls";

    private List<Client> clients;

    private boolean isGetFreeCount;

    public List<SelectItem> getAllClientsAsSelectItems() {
        if (clients == null) clients = gf().getClients();

        List<SelectItem> result = new ArrayList<SelectItem>();
        for (Client client : clients) {
            if (client.getManagerId() != null && getSecurityBean().isManagerCorrect(client.getManagerId()))
                result.add(new SelectItem(client.getId(), client.getCompanyName()));
        }
        return result;
    }

    private GoodType buildGoodTypeTree() {
        List<GoodType> nodesList = gf().getGoodTypes();

        for (GoodType gt : nodesList) gt.getChildren().clear();

        GoodType rootGroodType = null;
        for (int i = 0; i < nodesList.size(); i++) {
            GoodType n1 = nodesList.get(i);
            if (n1.getId() == 1) rootGroodType = n1;

            for (int j = 0; j < nodesList.size(); j++) {
                if (i == j) continue;

                GoodType n2 = nodesList.get(j);
                if (n2.getParentId() == n1.getId()) {
                    n1.getChildren().add(n2);
                    n2.setParent(n1);
                }
            }
        }

        rootGroodType.setLevel(0);
        rootGroodType.setParent(rootGroodType);

        return rootGroodType;
    }

    private List<GoodType> goodTypes = null;

    public List<GoodType> getGoodTypes() {
        if (goodTypes == null) {
            goodTypes = buildGoodTypeTree().collectNotDeleted();
        }
        return goodTypes;
    }

    /**
     * Идентификатор клиента, для которого выгружаем остатки
     */
    private long clientId = -1;

    /**
     * Идентификатор клиента, для которого выгружаем остатки
     */
    public void setClientId(long clientId) {
        this.clientId = clientId;
    }

    /**
     * Идентификатор клиента, для которого выгружаем остатки
     */
    public long getClientId() {
        return clientId;
    }

    public void doExport() {
        try {
            //  Проверяем, что работает groovy
            gf().createIfNotExists(TEMP_CATALOG, DEFAULT_TEMP_CATALOG);
            String tempCatalog = gf().get(TEMP_CATALOG);
            logger.debug("Temp catalog is: " + tempCatalog);

            gf().createIfNotExists(TEMPLATE_PATH, TEMPLATE_PATH_DEFAULT);
            String templatePath = gf().get(TEMPLATE_PATH);
//            String templatePath = "/home/sazonov/templates/rests_template3.xls";
            logger.debug("Template path is: " + templatePath);

            String uid = UUID.randomUUID().toString();
            logger.debug("Report uid is: " + uid);

            String tempFile = tempCatalog + File.separator + uid + ".xls";
            logger.debug("Temp file is: " + tempFile);

            generateReport(gf(), clientId, templatePath, tempFile);
            logger.debug("Generated successfuly!");
            HttpServletRequest request = (HttpServletRequest) getFacesContext().getExternalContext().getRequest();

            logger.debug("Publishing...");
            String link = FilePublisher.retrieveFile(request, tempFile, uid + ".xls");
            logger.debug("Published to: " + link);

            HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
            response.sendRedirect(link);
        } catch (IOException e) {
            logger.debug(e);
            facesError("Невозможно выполнить выгрузку данных");
        }
    }

    public void doOrderExport(long clientId, long orderId) {
        try {
            //  Проверяем, что работает groovy
            gf().createIfNotExists(TEMP_CATALOG, DEFAULT_TEMP_CATALOG);
            String tempCatalog = gf().get(TEMP_CATALOG);
            logger.debug("Temp catalog is: " + tempCatalog);

            gf().createIfNotExists(TEMPLATE_PATH, TEMPLATE_PATH_DEFAULT);
            String templatePath = gf().get(TEMPLATE_PATH);
//            String templatePath = "/home/sazonov/templates/rests_template3.xls";
            logger.debug("Template path is: " + templatePath);

            String uid = UUID.randomUUID().toString();
            logger.debug("Report uid is: " + uid);

            String tempFile = tempCatalog + File.separator + uid + ".xls";
            logger.debug("Temp file is: " + tempFile);

            generateOrderReport(gf(), clientId, orderId, templatePath, tempFile);
            logger.debug("Generated successfuly!");
            HttpServletRequest request = (HttpServletRequest) getFacesContext().getExternalContext().getRequest();

            logger.debug("Publishing...");
            String link = FilePublisher.retrieveFile(request, tempFile, uid + ".xls");
            logger.debug("Published to: " + link);

            HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
            response.sendRedirect(link);
        } catch (IOException e) {
            logger.debug(e);
            facesError("Невозможно выполнить выгрузку данных");
        }
    }

    public static void main(String[] args) throws IOException {
        generateReport(new Goodfil(), 918, "C:\\template.xls", "C:\\result.xls");
    }

    /**
     * Выгрузка остатков для клиента
     *
     * @param clientId     идентификатор клиента
     * @param templatePath путь к шаблону файла
     * @param link         ссылка, по которой должен быть опубликован файл
     */
    public static synchronized void generateReport(Goodfil gf, long clientId, String templatePath, String link) throws IOException {
        Logger logger = Logger.getLogger("RESTS_EXPORTER");

        if (gf == null) {
            gf = GoodfilFactory.getInstance();
        }
        Workbook wb = new HSSFWorkbook(new FileInputStream(templatePath));
        Sheet sheet1 = wb.getSheetAt(0);

        logger.debug("Generating report ---");
        logger.debug("  clientId: " + clientId);
        Client client = gf.getClientById(clientId);
        logger.debug("  client name: " + client.getCompanyName());
        Set<Long> visibleBrands = gf.getVisibleBrandsForClient(clientId);
        logger.debug("  visible brands: " + visibleBrands);

        //  Сначала обходим группы товаров
        List<Good> result = new ArrayList<Good>();
        for (GoodType goodType : gf.getGoodTypes()) {
            if (goodType.isRoot()) continue;
            if (goodType.isDeleted()) continue;
            if (goodType.getBrandId() == null) continue;
            if (!visibleBrands.contains(goodType.getBrandId().longValue())) continue;

            List<Good> goods = gf.getGoodsWithPrices2(0, Integer.MAX_VALUE, goodType.getId());

            //  Теперь обходим товары из этой группы товаров
            for (Good good : goods) {
                //  Удаленные товары нас не интересуют
                if (good.isDeleted()) continue;

                good.setGoodType(goodType);
                result.add(good);
            }
        }

        sheet1.getRow(1).getCell(0).setCellType(Cell.CELL_TYPE_STRING);
        sheet1.getRow(1).getCell(0).setCellValue(client.getCompanyName() + ", выгрузка от " + new SimpleDateFormat("dd.MM.yyyy").format(Calendar.getInstance().getTime()));

        logger.debug("goods count: " + result.size());

        Collections.sort(result, new Comparator<Good>() {
            public int compare(Good good, Good good1) {
                return good.getCode().compareTo(good1.getCode());
            }
        });

        Set<String> admin_errors = new LinkedHashSet<String>();
        for (Good good : result) {
            if (good.getBrandId() == null) {
                admin_errors.add("Good " + good.getCode() + " has null brandId, but was included into rests for client: " + clientId);
                continue;    //  У клиента нет колонки для этого брэнда
            }

            Long clientGroupId = client.getClientgroupIdByBrandId(good.getBrandId());
            if (clientGroupId == null) {
                admin_errors.add("Client " + clientId + " has not a column for brand " + good.getBrandId() + ", but this brand was included into rests");
                continue;    //  У клиента нет колонки для этого брэнда
            }

            if (!good.getPricesMap().containsKey(clientGroupId)) {
                admin_errors.add("Good " + good.getCode() + " has zero price for clientgroup " + clientGroupId + ", but it was included into rests");
                continue;  //  На данный товар нет цены в данной колонке
            }
            Float price = good.getPricesMap().get(clientGroupId);
            if (price == null) {
                admin_errors.add("Good2 " + good.getCode() + " has zero price for clientgroup " + clientGroupId + ", but it was included into rests");
                continue;
            }

            Row row = sheet1.createRow(6 + result.indexOf(good));
            Cell cell = null;

            //  Наименование группы
            cell = row.createCell(0);
            cell.setCellType(Cell.CELL_TYPE_STRING);
            cell.setCellValue(good.getGoodType().getName());

            //  Код товара
            cell = row.createCell(1);
            cell.setCellType(Cell.CELL_TYPE_STRING);
            cell.setCellValue(good.getCode());

            //  Наименование товара
            cell = row.createCell(2);
            cell.setCellType(Cell.CELL_TYPE_STRING);
            cell.setCellValue(good.getName());

            //  о.е.
            cell = row.createCell(3);
            cell.setCellType(Cell.CELL_TYPE_STRING);
            cell.setCellValue(good.getOe());

            //  Цена
            cell = row.createCell(4);
            cell.setCellType(Cell.CELL_TYPE_STRING);
            if (price > 0) {
                cell.setCellValue(String.valueOf(price));
            } else {
                cell.setCellValue("");
            }

            //  Свободный остаток
            cell = row.createCell(5);

            int rest = good.getCount() - good.getReservedCount();
            if (rest > getViewGoodsCount(gf)) {
                cell.setCellType(Cell.CELL_TYPE_STRING);
                cell.setCellValue("> 200");
            }
            else if(rest < 0){
                cell.setCellType(Cell.CELL_TYPE_STRING);
                cell.setCellValue(0);
            }
            else {
                cell.setCellType(Cell.CELL_TYPE_NUMERIC);
                cell.setCellValue(rest);
            }

            //  КОличество в групповой таре
            cell = row.createCell(6);
            cell.setCellType(Cell.CELL_TYPE_STRING);
            cell.setCellValue(good.getPackageCount());

            //  Количество для заказа
            cell = row.createCell(7);
            cell.setCellType(Cell.CELL_TYPE_NUMERIC);
            cell.setCellValue(0);
        }

        if (admin_errors.size() > 0) {
            StringBuilder sb = new StringBuilder();
            sb.append("ОШИБКИ ПРИ ВЫГРУЗКЕ ОСТАТКОВ");
            for (String s : admin_errors) {
                sb.append(s);
                sb.append("\n");
            }
            try {
                gf.sendEmailMsg(gf.get("admin.mail"), sb.toString());
            } catch (MailerException me) {
                logger.error(me);
                me.printStackTrace();
            }
        }

        FileOutputStream fos = new FileOutputStream(link);
        wb.write(fos);
        fos.flush();
        fos.close();
    }

    /**
     * Выгрузка заказа в Exel
     *
     * @param clientId     идентификатор клиента
     * @param orderId       индентификатор заказа
     * @param templatePath путь к шаблону файла
     * @param link         ссылка, по которой должен быть опубликован файл
     */
    public static synchronized void generateOrderReport(Goodfil gf, long clientId, long orderId, String templatePath, String link) throws IOException {
        Logger logger = Logger.getLogger("RESTS_EXPORTER");

        if (gf == null) {
            gf = GoodfilFactory.getInstance();
        }
        Workbook wb = new HSSFWorkbook(new FileInputStream(templatePath));
        Sheet sheet1 = wb.getSheetAt(0);

        logger.debug("Generating report ---");
        logger.debug("  clientId: " + clientId);
        Client client = gf.getClientById(clientId);
        logger.debug("  client name: " + client.getCompanyName());
        Set<Long> visibleBrands = gf.getVisibleBrandsForClient(clientId);
        logger.debug("  visible brands: " + visibleBrands);

        //  Сначала обходим группы товаров
        List<Good> result = new ArrayList<Good>();
        for (GoodType goodType : gf.getGoodTypes()) {
            if (goodType.isRoot()) continue;
            if (goodType.isDeleted()) continue;
            if (goodType.getBrandId() == null) continue;
            if (!visibleBrands.contains(goodType.getBrandId().longValue())) continue;

            List<Good> goods = gf.getGoodsWithPrices2(0, Integer.MAX_VALUE, goodType.getId());

            //  Теперь обходим товары из этой группы товаров
            for (Good good : goods) {
                //  Удаленные товары нас не интересуют
                if (good.isDeleted()) continue;

                good.setGoodType(goodType);
                result.add(good);
            }
        }

        sheet1.getRow(1).getCell(0).setCellType(Cell.CELL_TYPE_STRING);
        sheet1.getRow(1).getCell(0).setCellValue(client.getCompanyName() + ", выгрузка от " + new SimpleDateFormat("dd.MM.yyyy").format(Calendar.getInstance().getTime()));

        logger.debug("goods count: " + result.size());

        Collections.sort(result, new Comparator<Good>() {
            public int compare(Good good, Good good1) {
                return good.getCode().compareTo(good1.getCode());
            }
        });

        Set<String> admin_errors = new LinkedHashSet<String>();
        List<OrderItem> orderItems=gf.getOrderItemsByOrderId(orderId);
        for (Good good : result) {
            if (good.getBrandId() == null) {
                admin_errors.add("Good " + good.getCode() + " has null brandId, but was included into rests for client: " + clientId);
                continue;    //  У клиента нет колонки для этого брэнда
            }

            Long clientGroupId = client.getClientgroupIdByBrandId(good.getBrandId());
            if (clientGroupId == null) {
                admin_errors.add("Client " + clientId + " has not a column for brand " + good.getBrandId() + ", but this brand was included into rests");
                continue;    //  У клиента нет колонки для этого брэнда
            }

            if (!good.getPricesMap().containsKey(clientGroupId)) {
                admin_errors.add("Good " + good.getCode() + " has zero price for clientgroup " + clientGroupId + ", but it was included into rests");
                continue;  //  На данный товар нет цены в данной колонке
            }
            Float price = good.getPricesMap().get(clientGroupId);
            if (price == null) {
                admin_errors.add("Good2 " + good.getCode() + " has zero price for clientgroup " + clientGroupId + ", but it was included into rests");
                continue;
            }

            Row row = sheet1.createRow(6 + result.indexOf(good));
            Cell cell = null;

            //  Наименование группы
            cell = row.createCell(0);
            cell.setCellType(Cell.CELL_TYPE_STRING);
            cell.setCellValue(good.getGoodType().getName());

            //  Код товара
            cell = row.createCell(1);
            cell.setCellType(Cell.CELL_TYPE_STRING);
            cell.setCellValue(good.getCode());

            //  Наименование товара
            cell = row.createCell(2);
            cell.setCellType(Cell.CELL_TYPE_STRING);
            cell.setCellValue(good.getName());

            //  о.е.
            cell = row.createCell(3);
            cell.setCellType(Cell.CELL_TYPE_STRING);
            cell.setCellValue(good.getOe());

            //  Цена
            cell = row.createCell(4);
            cell.setCellType(Cell.CELL_TYPE_STRING);
            if (price > 0) {
                cell.setCellValue(String.valueOf(price));
            } else {
                cell.setCellValue("");
            }

            //  Свободный остаток
            cell = row.createCell(5);

            int rest = good.getCount() - good.getReservedCount();
            if (rest > getViewGoodsCount(gf)) {
                cell.setCellType(Cell.CELL_TYPE_STRING);
                cell.setCellValue("> 200");
            } else {
                cell.setCellType(Cell.CELL_TYPE_NUMERIC);
                cell.setCellValue(rest);
            }

            //  КОличество в групповой таре
            cell = row.createCell(6);
            cell.setCellType(Cell.CELL_TYPE_STRING);
            cell.setCellValue(good.getPackageCount());

            //  Количество для заказа
            cell = row.createCell(7);
            cell.setCellType(Cell.CELL_TYPE_NUMERIC);
            for(OrderItem item:orderItems){
                if(good.getId()==item.getGoodId()){
                    cell.setCellValue(item.getCount());
                    System.out.println(item.getGoodId()+" "+item.getCount());
                    break;
                } else {
                    cell.setCellValue(0);
                }
            }

        }

        if (admin_errors.size() > 0) {
            StringBuilder sb = new StringBuilder();
            sb.append("ОШИБКИ ПРИ ВЫГРУЗКЕ ОСТАТКОВ");
            for (String s : admin_errors) {
                sb.append(s);
                sb.append("\n");
            }
            try {
                gf.sendEmailMsg(gf.get("admin.mail"), sb.toString());
            } catch (MailerException me) {
                logger.error(me);
                me.printStackTrace();
            }
        }

        FileOutputStream fos = new FileOutputStream(link);
        wb.write(fos);
        fos.flush();
        fos.close();
    }

    
    public List<RestsExport> getRestsExports() {
        List<RestsExport> restsExports = gf().getRestsExports();
        for (RestsExport restsExport : restsExports) {
            restsExport.setClient(gf().getClientById(restsExport.getClientId()));
            restsExport.setCrontabHuman(RestsExportManager.getValueByKey(restsExport.getCrontab()));
        }
        return restsExports;
    }

    /**
     * Полчить максимальное количество товара для отображения ("в наличии")
     */
    public static int getViewGoodsCount(Goodfil gf) {
        gf.createIfNotExists("view.goods.count", 200);
        return gf.getInt("view.goods.count");
    }

    public RestsStat getRestsStat() {
        return RestsExportBean.collectRestsStat(gf());
    }

    public static RestsStat collectRestsStat(Goodfil gf) {
        BigDecimal weight = new BigDecimal(0);
        BigDecimal volume = new BigDecimal(0);
        int positionsCount = 0;
        int positionsWithoutWeightCount = 0;
        int positionsWithoutVolumeCount = 0;

        StringBuilder positionsWithoutWeightString = new StringBuilder("");
        StringBuilder positionsWithoutVolumeString = new StringBuilder("");

        List<Good> goods = gf.getGoods(0, Integer.MAX_VALUE, null);
        for (Good good : goods) {
            if (good.getCount() > 0) {
                if (good.getWeight() > 0) {
                    weight = weight.add(new BigDecimal(good.getWeight() * good.getCount()));
                } else {
                    positionsWithoutWeightCount++;
                    positionsWithoutWeightString.append(good.getCode());
                    positionsWithoutWeightString.append(' ');
                }

                if (good.getVolume() > 0) {
                    volume = volume.add(new BigDecimal(good.getVolume() * good.getCount()));
                } else {
                    positionsWithoutVolumeCount++;
                    positionsWithoutVolumeString.append(good.getCode());
                    positionsWithoutVolumeString.append(' ');
                }

                positionsCount++;
            }
        }

        weight = weight.setScale(2, BigDecimal.ROUND_HALF_UP);
        volume = volume.setScale(5, BigDecimal.ROUND_HALF_UP);

        return new RestsStat(weight, volume, positionsCount, positionsWithoutWeightCount, positionsWithoutVolumeCount, positionsWithoutWeightString.toString(), positionsWithoutVolumeString.toString());
    }

    public boolean isGetFreeCount() {
        return isGetFreeCount;
    }

    public void setGetFreeCount(boolean isGetFreeCount) {
        this.isGetFreeCount = isGetFreeCount;
    }

    /**
     * Информация о товаре на складе
     */
    public static class RestsStat {
        /**
         * Суммарный вес товара на складе
         */
        private final BigDecimal weight;

        /**
         * Суммарный объем товара на складе
         */
        private final BigDecimal volume;

        /**
         * Количество позиций, которые есть на складе
         */
        private final int positionsCount;

        /**
         * Количество позиций, которые есть на складе, но для которых не указан вес
         */
        private final int positionsWithoutWeightCount;

        /**
         * Количество позиций, которые есть на складе, но для которых не указан объем
         */
        private final int positionsWithoutVolumeCount;

        /**
         * Перечисление через \n позиций, для которых не указан вес
         */
        private final String positionsWithoutWeightString;

        /**
         * Перечисление через \n позиций, для которых не указан объем
         */
        private final String positionsWithoutVolumeString;

        public RestsStat(BigDecimal weight, BigDecimal volume, int positionsCount, int positionsWithoutWeightCount, int positionsWithoutVolumeCount,
                         String positionsWithoutWeightString, String positionsWithoutVolumeString) {
            this.weight = weight;
            this.volume = volume;
            this.positionsCount = positionsCount;
            this.positionsWithoutWeightCount = positionsWithoutWeightCount;
            this.positionsWithoutVolumeCount = positionsWithoutVolumeCount;
            this.positionsWithoutWeightString = positionsWithoutWeightString;
            this.positionsWithoutVolumeString = positionsWithoutVolumeString;
        }

        public BigDecimal getWeight() {
            return weight;
        }

        public BigDecimal getVolume() {
            return volume;
        }

        public int getPositionsCount() {
            return positionsCount;
        }

        public int getPositionsWithoutWeightCount() {
            return positionsWithoutWeightCount;
        }

        public int getPositionsWithoutVolumeCount() {
            return positionsWithoutVolumeCount;
        }

        public String getPositionsWithoutWeightString() {
            return positionsWithoutWeightString;
        }

        public String getPositionsWithoutVolumeString() {
            return positionsWithoutVolumeString;
        }


    }
}
